﻿using System;
using System.Collections.Generic;
using System.Text;

namespace libSecuritySystem
{
    public class k_n_ThresholdModel
    {
        public int MaxMember = 10;

        private long InvisibleSecret = 0;   //Be assigned when constructs members information
                                            //Then may be filled in the routing servers password

        public long GlobalSecret; //Obtains the recovery secret to be compaired with InvisibleSecret

        private long Secret;

        public long Secret1
        {
            get { return Secret; }
            set { Secret = value; }
        }

        private int _n;

        public int N
        {
            get { return _n; }
            set
            {
                
            }
        }

        private int _k;

        public int K
        {
            get { return _k; }
            set
            {
               
            }
        }

        public k_n_ThresholdModel(int n, int k, int max = 10)
        {
            if ((int)max > 0)
            {
                this.MaxMember = max;
            }
            else {
                throw new Exception("The max input is invalid. It must satisfy (max > 0).");
            }

            if ((int)n > 0 && (int)n <= MaxMember)
            {
                this._n = n;
            }
            else
            {
                throw new Exception("The n input is invalid. It must satisfy (n > 0 && n <= max).");
            }

            if ((int)k > 0 && (int)k <= this._n)
            {
                this._k = k;
            }
            else
            {
                throw new Exception("The k input is invalid.  It must satisfy (k > 0 && k <= n)");
            }
        }

        private List<int> ConstructThreholds(ref long min, ref long max)
        {
            List<int> ms = new List<int>();
            List<int> temp = new List<int>();
            do
            {
                temp.Clear();
                min = max = 1;
                for (int i = 0; i < this._n; i++)
                {
                    int m = 0;
                    do
                    {
                        Random r = new Random();
                        m = r.Next(50, 1000);
                    }
                    while (!Crytography.CheckRelativePrimeWithArray(temp, m));

                    temp.Add(m);
                    if (i < this._k)
                        min *= m;
                    else
                        max *= m;
                }
            }
            while ((min <= max) || (min > max && min - max < 10));
            ms.AddRange(temp);
            return ms;
        }

        public List<MemberInfo> ConstructSecret()
        {
            List<MemberInfo> list = new List<MemberInfo>();
            try
            {
                long min, max;
                min = max = 1;
                List<int> thresholds = this.ConstructThreholds(ref min, ref max);
                //this.Secret = (min + max) / 2;
                InvisibleSecret = (min + max) / 2;

                //Construct M
                long M = min * max;
                //Construct Ii
                for (int i = 0; i < this._n; i++)
                {
                    int I = (int)(InvisibleSecret % (long)thresholds[i]);

                    MemberInfo mem = new MemberInfo(thresholds[i], I, M);
                    list.Add(mem);
                }
                return list;
            }
            catch (Exception ex)
            {
                InvisibleSecret = 0;
                return null;
            }

        }

        public bool RecoverySecret(List<MemberInfo> mems)
        {
            long Mi, SecretTemp;
            SecretTemp = 0;
            long modMul = 1;
            int Ni;
            for (int i = 0; i < mems.Count; i++)
            {
                Mi = mems[i].M / mems[i].Mi;
                Ni = Crytography.ModInverseIn_Zm(Mi, mems[i].Mi);

                SecretTemp += mems[i].I * Mi * Ni;
                modMul *= mems[i].Mi;
            }
            SecretTemp = SecretTemp % (modMul);
            if (SecretTemp != InvisibleSecret)
                return false;
            return true;
        }

        public long RecoverySecret_1(List<MemberInfo> mems)
        {
            long Mi, SecretTemp;
            SecretTemp = 0;
            long modMul = 1;
            int Ni;
            for (int i = 0; i < mems.Count; i++)
            {
                Mi = mems[i].M / mems[i].Mi;
                Ni = Crytography.ModInverseIn_Zm(Mi, mems[i].Mi);

                SecretTemp += mems[i].I * Mi * Ni;
                modMul *= mems[i].Mi;
            }
            SecretTemp = SecretTemp % (modMul);

            return SecretTemp;
        }
    }
}
